package com.share.cloudStorage.hdfs;
import com.share.cloudStorage.files.controller.FilesController;
import com.share.cloudStorage.files.model.Files;
import com.share.cloudStorage.users.model.Users;
import lombok.extern.slf4j.Slf4j;
import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.fs.*;
import org.apache.commons.io.IOUtils;
import org.springframework.http.HttpHeaders;
import org.springframework.http.ResponseEntity;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;
import javax.servlet.http.HttpServletResponse;
import java.io.*;
import java.net.URI;
import java.net.URISyntaxException;
import java.util.Map;
import java.util.zip.ZipEntry;
import java.util.zip.ZipOutputStream;



@Component
@Slf4j
public class HdfsClient {

    private static final String uri="hdfs://172.20.249.148:9000/";
    private FileSystem fileSystem;
    //初始化客户端
    public HdfsClient() throws URISyntaxException, IOException, InterruptedException {
        Configuration configuration = new Configuration();
        configuration.set("fs.defaultFs",uri);
        fileSystem=FileSystem.get(new URI(uri),configuration,"root");
    }

    //上传文件
    public String uploadFile(InputStream inputStream, String folder,Files files) throws IOException {
        String filePath = "/" + folder + "/"+files.getFilePath();
        log.info("文件路径:{}",filePath);
        Path path = new Path(filePath);
      try(FSDataOutputStream outputStream = fileSystem.create(path)){
            IOUtils.copyLarge(inputStream,outputStream);
      }
      return filePath;
    }

    //创建文件夹
    public void createFolder(String filePath) throws IOException {
        Path path = new Path(filePath);
        fileSystem.mkdirs(path);
        //hdfs下载文件
    }

    //是否存在相同文件夹
    public Boolean folderExists(String filepath) {
        // 确保路径格式正确

        try {
            // 检查路径是否存在
            Path path = new Path(filepath);
            if (fileSystem.exists(path)) {
                log.info("Path does not exist: {}", filepath);
                return true;
            }
        } catch (IOException e) {
            log.error("Error checking folder existence: {}", e.getMessage(), e);
        }

        return false;
    }


    public void downLoad (String path,HttpServletResponse response) throws IOException {
        Path filePath = new Path(path);// 使用 HDFS 配置
        long fileSize = fileSystem.getFileStatus(filePath).getLen();  // 获取文件大小

        // 设置响应头
        response.setContentType("application/octet-stream");
        response.setHeader(HttpHeaders.CONTENT_DISPOSITION, "attachment; filename=\"" + filePath.getName() + "\"");
        response.setHeader(HttpHeaders.CONTENT_LENGTH, String.valueOf(fileSize));

        try (BufferedInputStream inputStream = new BufferedInputStream(fileSystem.open(filePath));
             BufferedOutputStream outputStream = new BufferedOutputStream(response.getOutputStream())) {

            byte[] buffer = new byte[8192];  // 设置合适的缓冲区大小
            int bytesRead;
            while ((bytesRead = inputStream.read(buffer)) != -1) {
                outputStream.write(buffer, 0, bytesRead);
//                // 通知前端进度（每传输 1% 就输出一次进度）
//                if (bytesSent * 100 / fileSize % 1 == 0) {
//                    // 发送下载进度 (前端可以通过 HTTP 状态码或者 JSON 响应获取)
//                    System.out.println("Progress: " + (bytesSent * 100 / fileSize) + "%");
//                }
            }
            outputStream.flush();
        }
    }
    // 删除文件夹
    public void deleteFolder(Users users) throws IOException {
        Path path = new Path("/" + users.getMobile());
        if (fileSystem.exists(path)) {
            fileSystem.delete(path, true);  // 删除文件夹及其内容
        }
    }
    //重命名文件
    public void reName(Files files,String folder) throws IOException {
        String originalPath="/"+folder+"/"+files.getFilePath();
        String newPath="/"+folder+"/"+files.getNewFilePath();
        log.info("----------:{}",originalPath + newPath);
        fileSystem.rename(new Path(originalPath),new Path(newPath));
    }
    //文件夹的下载
        public ResponseEntity downloadHdfsZip(String path, HttpServletResponse response,Map<String, Integer> downloadProgressMap,String taskId) throws IOException, InterruptedException {

            Integer count = 0;
            log.info("path:=========={}", path);
            // 目标 HDFS 文件夹路径
            Path hdfsFolderPath = new Path(path);
            // 获取文件夹中的所有文件/子文件夹
            FileStatus[] fileStatuses = fileSystem.listStatus(hdfsFolderPath);
            long totalSize = getTotalSize(fileStatuses);
            Long transferredSize = 0l;
            // 创建 HTTP 响应流
            response.setContentType("application/zip");
            response.setHeader(HttpHeaders.CONTENT_DISPOSITION, "attachment; filename=archive.zip");
            // 使用 ZipOutputStream 将压缩文件直接写入响应流
            try (ZipOutputStream zipOut = new ZipOutputStream(response.getOutputStream())) {
                // 递归遍历文件夹内容并将其添加到 ZIP 包中
                for (FileStatus fileStatus : fileStatuses) {
                    addFileToZip(fileStatus, hdfsFolderPath, zipOut,transferredSize,totalSize,taskId,count);  // 递归调用，处理每个文件和目录
                }
                zipOut.finish(); // 确保压缩流完成
            }
            downloadProgressMap.put(taskId, 100);
            return ResponseEntity.ok().build();
        }

        private long addFileToZip(FileStatus fileStatus, Path basePath, ZipOutputStream zipOut,Long transferredSize,long totalSize,String taskId,Integer count) throws IOException {
            Path filePath = fileStatus.getPath();
            // 获取相对路径，去掉 HDFS URI 部分
            String relativePath = getRelativePath(basePath, filePath);
            log.info("相对路径：{}",relativePath);
            // 如果是目录，创建一个空的目录条目
            if (fileStatus.isDirectory()) {
                // 目录条目的路径名以 '/' 结尾，表示这是一个目录
                relativePath = relativePath.endsWith("/") ? relativePath : relativePath + "/";
                ZipEntry zipEntry = new ZipEntry(relativePath);
                zipOut.putNextEntry(zipEntry);
                zipOut.closeEntry(); // 对于目录，直接关闭条目，不写任何内容

                // 递归遍历该目录中的文件/子文件夹
                FileStatus[] folderStatuses = fileSystem.listStatus(filePath);
                for (FileStatus folderStatus : folderStatuses) {
                    long l = addFileToZip(folderStatus, basePath, zipOut, transferredSize, totalSize, taskId,count);
                    transferredSize=l;// 递归处理每个文件或子目录
                }
            } else {
                // 如果是文件，创建 ZIP 条目并写入文件内容
                ZipEntry zipEntry = new ZipEntry(relativePath);
                zipOut.putNextEntry(zipEntry);

                try (InputStream inputStream = fileSystem.open(filePath)) {
                    byte[] buffer = new byte[1024];
                    int length;
                    while ((length = inputStream.read(buffer)) > 0) {
                        zipOut.write(buffer, 0, length);
                        transferredSize += length;
                        count++;
                        int progress = (int) (transferredSize * 100 / totalSize);
                        if (count%10000==0){
                            log.info("count:{}",count);
                            FilesController.downloadProgressMap.put(taskId, progress);
                        }
                         // 更新进度
                    }
                }
                zipOut.closeEntry();
            }
            return transferredSize;
        }


        private String getRelativePath(Path basePath, Path filePath) {
            // 获取相对路径并避免错误地将 URI 部分包含在内
            String relativePath = basePath.getName() != null
                    ? filePath.makeQualified(fileSystem.getUri(), fileSystem.getWorkingDirectory()).toUri().getPath().substring(basePath.toString().length() + 1)
                    : filePath.toString();
            return relativePath;
        }
        private long getTotalSize(FileStatus[] fileStatuses) throws IOException {
            long totalSize = 0;
            for (FileStatus fileStatus : fileStatuses) {
                if (fileStatus.isDirectory()) {
                    // 递归计算目录下所有文件的大小
                    FileStatus[] folderStatuses = fileSystem.listStatus(fileStatus.getPath());
                    totalSize += getTotalSize(folderStatuses);
                } else {
                    totalSize += fileStatus.getLen();  // 累加文件的大小
                }
            }
            return totalSize;
        }

    //将分享的文件保存到自己文件夹下
    public void moveFile(String sharePath, String acceptPath) throws IOException {
        log.info("分享路径和接受路径{}，{}",sharePath,acceptPath);
        Path srcPath = new Path("/" + sharePath);
        Path destPath = new Path("/" +acceptPath);

        if (!fileSystem.exists(srcPath)){
            throw new IOException("源路径 " + sharePath + " 不存在！");
        }

        if (!fileSystem.exists(destPath)) {
            fileSystem.mkdirs(destPath);
        }

        if (!FileUtil.copy(fileSystem, srcPath, fileSystem, destPath, false, fileSystem.getConf())) {
            throw new IOException("复制文件夹失败：" + sharePath + " -> " + acceptPath);
        }
    }
    @Scheduled(cron = "0 0 0/1 * * ?")
    public void deleteMapCache(){
        FilesController.downloadProgressMap.entrySet().removeIf(entry -> entry.getValue() == 100);
    }
}
